// Copyright (C) 2025 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::ffi::CStr;

use crate::{
    ffi::{
        g_monad_event_content_type_names, monad_event_content_type, monad_event_ring,
        monad_event_ring_check_content_type, MONAD_EVENT_CONTENT_TYPE_COUNT,
        MONAD_EVENT_CONTENT_TYPE_NONE,
    },
    EventDescriptorInfo,
};

/// Used to decode events from an event ring.
///
/// Events in an [`EventRing`](crate::EventRing) are written by an event ring writer using a known
/// ABI. Each event is encoded into a u8 payload which must be decoded by consumers. Types that
/// implement this trait describe one of said known ABIs by defining the metadata associated with
/// the ABI, the [`EventDecoder::FlowInfo`] associated with each event, and the event type
/// [`EventDecoder::Event`] itself along with its zero-copy variant [`EventDecoder::EventRef`]. This
/// enables the [`EventReader`](crate::EventReader) to produce typed events.
pub trait EventDecoder: 'static {
    /// An integer specifying the event ring content type.
    fn ring_content_ctype() -> monad_event_content_type;
    /// A human-readable name for the `ring_ctype`.
    ///
    /// # Panics
    ///
    /// Panics if [`ring_content_ctype()`](EventDecoder::ring_content_ctype) is invalid.
    fn ring_ctype_name() -> String {
        let content_ctype = Self::ring_content_ctype();

        assert!(MONAD_EVENT_CONTENT_TYPE_NONE < content_ctype);
        assert!(content_ctype < MONAD_EVENT_CONTENT_TYPE_COUNT);

        let description_cstr =
            unsafe { CStr::from_ptr(g_monad_event_content_type_names[content_ctype as usize]) };

        description_cstr.to_str().unwrap().to_string()
    }

    /// An autogenerated hash derived from the underlying C type defs.
    ///
    /// This hash is used for versioning control and enforces that event ingested through
    /// [`EventReader`](crate::EventReader) have the same underlying ABI as the event ring writer.
    fn ring_schema_hash() -> &'static [u8; 32];

    /// Used to check that the event ring matches this [`EventDecoder`].
    fn check_ring_content_type(c_event_ring: &monad_event_ring) -> Result<(), String> {
        monad_event_ring_check_content_type(
            c_event_ring,
            Self::ring_content_ctype(),
            Self::ring_schema_hash(),
        )
    }

    /// The metadata associated with each [`Event`](EventDecoder::Event).
    ///
    /// Every event descriptor stores a metadata array which can be used to attach metadata to
    /// events. This associated type specifies what the shape of that metadata is for this event
    /// ring type.
    type FlowInfo;

    /// Defines how to convert the raw user info from an event descriptor to the associated
    /// [`Self::FlowInfo`](EventDecoder::FlowInfo) type.
    fn transmute_flow_info(user: [u64; 4]) -> Self::FlowInfo;

    /// The rust-native type of the elements produced by an event ring.
    type Event;
    /// A zero-copy view of the elements produced by an event ring.
    type EventRef<'ring>;

    /// Provides the zero-copy view [`Self::EventRef<'ring>`](EventDecoder::EventRef) from an
    /// event ring payload byte slice.
    fn raw_to_event_ref<'ring>(
        info: EventDescriptorInfo<Self>,
        bytes: &'ring [u8],
    ) -> Self::EventRef<'ring>
    where
        Self: Sized;

    /// Defines how to convert the zero-copy [`Self::EventRef<'ring>`](EventDecoder::EventRef) to
    /// the owned variant [`Self::Event`](EventDecoder::Event).
    fn event_ref_to_event<'ring>(event_ref: Self::EventRef<'ring>) -> Self::Event;
}

/// Event ring decoder used for ingesting events as raw byte data.
pub struct BytesDecoder;

impl EventDecoder for BytesDecoder {
    fn ring_content_ctype() -> monad_event_content_type {
        unreachable!()
    }

    fn ring_ctype_name() -> String {
        "raw".to_string()
    }

    fn ring_schema_hash() -> &'static [u8; 32] {
        unreachable!()
    }

    fn check_ring_content_type(_: &monad_event_ring) -> Result<(), String> {
        Ok(())
    }

    type FlowInfo = ();

    fn transmute_flow_info(_: [u64; 4]) -> Self::FlowInfo {}

    type Event = (u16, Box<[u8]>);
    type EventRef<'ring> = (u16, &'ring [u8]);

    fn raw_to_event_ref<'ring>(
        info: EventDescriptorInfo<Self>,
        bytes: &'ring [u8],
    ) -> Self::EventRef<'ring> {
        (info.event_type, bytes)
    }

    fn event_ref_to_event<'ring>((event_type, event_ref): Self::EventRef<'ring>) -> Self::Event {
        (event_type, event_ref.to_vec().into_boxed_slice())
    }
}
